package upm.pb4agent.
team5.common;

import java.util.Date;
import jade.content.Concept;
import jade.content.lang.Codec.CodecException;
import jade.content.onto.OntologyException;
import jade.core.AID;
import jade.core.behaviours.CyclicBehaviour;
import jade.domain.FIPAException;
import jade.lang.acl.ACLMessage;
import jade.util.leap.ArrayList;
import jade.util.leap.Iterator;
import jade.util.leap.List;
import jade.wrapper.StaleProxyException;
import upm.pb4agent.ontology.AssignBoat;
import upm.pb4agent.ontology.Dock;
import upm.pb4agent.ontology.DockDesignation;
import upm.pb4agent.ontology.EnteringDock;
import upm.pb4agent.ontology.LeavingDock;
import upm.pb4agent.ontology.RaceFinished;
import upm.pb4agent.ontology.RealRow;
import upm.pb4agent.ontology.Registration;
import upm.pb4agent.ontology.RegistrationForm;
import upm.pb4agent.ontology.Row;
import upm.pb4agent.ontology.Seat;
import upm.pb4agent.team5.enumeration.ConfigProperty;
import upm.pb4agent.team5.util.ConfigFile;
import upm.pb4agent.team5.util.RealRowWrapper;

public class BWatcherManageMessages extends CyclicBehaviour {
	private static final long serialVersionUID = -9030427864117772787L;
	private static final long WAIT_TIME_ROW = 200; // milliseconds
	
	private Watcher watcher = null;
	private AID refereeAID = null;
	private AID boatAID = null;
	private int x = 0;
	private int y = 0;
	private boolean[] crosssedDock = { true, false, false, false, false }; // Includes final line
	
	public BWatcherManageMessages(Watcher watcher) {
		this.watcher = watcher;
	}
	
	public void action() {
		ACLMessage msg = watcher.blockingReceive();

		try {
			Concept concept = watcher.getConcept(msg);
			if (refereeAID == null)
				refereeAID = watcher.findAID(Referee.DF_NAME);
			
			if (concept instanceof Registration) {
				assignBoat((Registration) concept);
			} else if (concept instanceof Row) {
				row((Row) concept, msg.getSender());
			}
		} catch (Exception e) {
			System.err.println(watcher.getLocalName() + ": Error managing race started message " + e.getMessage());
		}
		
	}
	
	private void assignBoat(Registration registration) throws StaleProxyException, InterruptedException, FIPAException, CodecException, OntologyException {
		RegistrationForm registrationForm = registration.getRegistrationForm();
		String dfName = Boat.DF_NAME + registrationForm.getTeamID().getValue();
		
		// Creating Boat agent
		boatAID = watcher.createAgent(Boat.class, dfName, null, true);
		
		// Creating AssignBoat
		AssignBoat assignBoat = fillAssignBoat(boatAID);

		// Adding RegistrationForm and Boat to Physics
		watcher.getWatcherPhysics().setRegistrationForm(registrationForm);
		watcher.getWatcherPhysics().setBoat(assignBoat.getBoat());
		
		// Sending AID to Rowers (AssignBoat)
		Iterator iterator = registrationForm.getAllDockDesignation();
		
		while (iterator.hasNext()) { // Extracting dock dessignation
			DockDesignation dockDess = (DockDesignation) iterator.next();
			AID rower = dockDess.getRowerID();
			
			watcher.send(assignBoat, rower, ACLMessage.INFORM);
			System.out.println(watcher.getLocalName() + ": Boat assigned to " + rower.getLocalName());
		}		
	}
	
	private AssignBoat fillAssignBoat(AID boatAID) {
		List seats = new ArrayList();
		for (int i=0; i<6; i++) {
			Seat seat = new Seat();
			seat.setSeatID(Boat.SEAT_ID[i]);
			seats.add(seat);
		}

		upm.pb4agent.ontology.Boat boat = new upm.pb4agent.ontology.Boat();
		boat.setID(boatAID);
		boat.setSeats(seats);
		
		AssignBoat assignBoat = new AssignBoat();
		assignBoat.setBoat(boat);
		
		return assignBoat;
	}
		
	private void row(Row row, AID rowerAID) throws CodecException, OntologyException, FIPAException, InterruptedException {
		// TODO: Validate row... Include logic for denying or accepting 
		RealRowWrapper realRowWrapper = watcher.getWatcherPhysics().realRow(row, rowerAID);
			
		long timeRow = Math.round(realRowWrapper.getTimeRow() * 1000); // milliseconds
		long actualTime = 0; // milliseconds
		while (timeRow < actualTime) {
			Thread.sleep(WAIT_TIME_ROW);
			actualTime += WAIT_TIME_ROW;
		}
			
		RealRow realRow = realRowWrapper.getRealRow();
		watcher.send(realRow, rowerAID, ACLMessage.INFORM); // Sending message to the rower
		watcher.send(realRow, boatAID, ACLMessage.INFORM); // Sending message to the boat
			
		calculateNewPosition(row.getBoat(), realRow.getX(), realRow.getY()); // Sends EnteringDock or RaceFinished if corresponds

		System.out.println(watcher.getLocalName() + ": Rowed " + "(x=" + realRow.getX() + 
				", y=" + realRow.getY() + ", timeRow=" + timeRow + ", position[" + 
				this.x +", " + this.y + "]) " + row.getBoat().getID().getLocalName());
	}
	
	private void calculateNewPosition(upm.pb4agent.ontology.Boat boat, int x, int y) throws CodecException, OntologyException {
		this.x += x;
		this.y += y;
		
		int circuitLength = Integer.parseInt(
				ConfigFile.getProperty(ConfigProperty.circuit_length.name())
			);
		
		float chunk = circuitLength / 4;
		int advance = (int) (this.y / chunk); 

		if (advance < 4 && !crosssedDock[advance]) { // EnteringDock
			sendEnteringDock(boat, Watcher.DOCK_NAME[advance], 1); // TODO Position should be something stored in a vector for example
			crosssedDock[advance] = true;
		} else if (advance == 4 && !crosssedDock[advance]) { // RaceFinished
			sendRaceFinished(boat, 1); // TODO Position should be something stored in a vector for example
			crosssedDock[advance] = true;
		}
	}
	
	private void sendEnteringDock(upm.pb4agent.ontology.Boat boat, String dockName, int position) throws CodecException, OntologyException {
		Dock dock = new Dock();
		dock.setName(dockName);

		EnteringDock enteringDock = new EnteringDock();
		enteringDock.setBoatAID(boat.getID());
		enteringDock.setDock(dock);
		enteringDock.setPosition(position); 
		enteringDock.setTimeStamp(Long.toString(new Date().getTime()));
		
		watcher.send(enteringDock, refereeAID, ACLMessage.INFORM);
	}
	
	@SuppressWarnings("unused")
	private void sendLeavingDock(upm.pb4agent.ontology.Boat boat, String dockName) throws CodecException, OntologyException {
		Dock dock = new Dock();
		dock.setName(dockName);

		LeavingDock leavingDock = new LeavingDock();
		leavingDock.setBoatAID(boat.getID());
		leavingDock.setDock(dock);
		
		watcher.send(leavingDock, refereeAID, ACLMessage.INFORM);
	}

	private void sendRaceFinished(upm.pb4agent.ontology.Boat boat, int position) throws CodecException, OntologyException {
		RaceFinished raceFinished = new RaceFinished();
		raceFinished.setBoatAID(boat.getID());
		raceFinished.setPosition(position);
		raceFinished.setTimeStamp(Long.toString(new Date().getTime()));
		
		watcher.send(raceFinished, refereeAID, ACLMessage.INFORM);
	}
	
}
